/** @file ar_chardev.h
  *
  * @brief This file contains ar_chardev driver specific defines etc
  *
  *  Copyright (C) 2024, Artosyn Technologies. ALL RIGHTS RESERVED.
  *
  *  This Information is proprietary to Artosyn Technologies and MAY NOT
  *  be copied by any method or incorporated into another program without
  *  the express written consent of Artosyn. This Information or any portion
  *  thereof remains the property of Artosyn. The Information contained herein
  *  is believed to be accurate and Artosyn assumes no responsibility or
  *  liability for its use in any way and conveys no license or title under
  *  any patent or copyright and makes no representation or warranty that this
  *  Information is free from patent or copyright infringement.
  *
  */
#ifndef __AR_CHARDEV_H__
#define __AR_CHARDEV_H__

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/skbuff.h>
#include <linux/semaphore.h>
#include <linux/sched.h>

/** Define ioctl */
#define AR_CMD_MAGIC 'A'

#define AR_CHARDEV_IOCTL_NET_GET_VERSION        _IO(AR_CMD_MAGIC, 0)    /** Get the version of net dev */
#define AR_CHARDEV_IOCTL_NET_HANDLE_NETIF       _IO(AR_CMD_MAGIC, 1)    /** Create/Destroy the net dev */
#define AR_CHARDEV_IOCTL_NET_RELEASE            _IO(AR_CMD_MAGIC, 2)    /** Release the char dev resource request by user */

#define AR_CHARDEV_IOCTL_GET_VERSION            _IO(AR_CMD_MAGIC, 10)   /** Get the version of driver */
#define VERSION_STR_LEN                         64

/** Structure for get version */
typedef struct _ar_chrdev_get_version_s {
    char version[VERSION_STR_LEN];
} ar_chrdev_get_version_t;

/** Major num of the char device */
#define AR_CHARDEV_MAJOR_NUM           (0)

/** Interface specific macros */
#define DRV_CHAR_MINOR_BASE             (0)
#define NET_CHAR_MINOR_BASE             (10)

/** Define sem lock/unlock wrapper */
#define mdev_req_lock(d)                down_interruptible(&d->req_lock)
#define mdev_req_unlock(d)              up(&d->req_lock)

/** Define lock/unlock wrapper */
#define mdev_spin_lock(d, f)            spin_lock_irqsave(&d->lock, f)
#define mdev_spin_unlock(d, f)          spin_unlock_irqrestore(&d->lock, f)

/** Define mutex lock/unlock wrapper */
#define mdev_mutex_lock(d)              mutex_lock_interruptible(&d->rlock)
#define mdev_mutex_unlock(d)            mutex_unlock(&d->rlock)

/** Length of device name */
#define DEV_NAME_LEN                    32


/** Length of module name */
#define AR_MODULE_NAME_LEN              32
/** module name */
#define AR_MODULE_NAME                  "ar_drv"

/** Running flags */
#define CHAR_DEV_UP         0
#define CHAR_DEV_RUNNING    1

/** Chmod and chown */
#define CDEV_SET_PERMISSION

/** Define dev type */
typedef enum _driver_chrdev_type {
    CHARDEV_TYPE_NET = 1,
    CHARDEV_TYPE_DRV,
    CHARDEV_TYPE_DEBUG,
    CHARDEV_TYPE_MAX
} driver_chrdev_type;

/**
 * @brief  Set scheduled timeout
 *
 * @param millisec       Time unit in ms
 *
 * @return                N/A
 */
static inline void
os_sched_timeout(u32 millisec)
{
    set_current_state(TASK_INTERRUPTIBLE);

    schedule_timeout((millisec * HZ) / 1000);
}

/** Define struct m_dev */
struct m_dev {
    /* Name of the char device */
    char name[DEV_NAME_LEN];
    /* Index of the device */
    int index;
    /* The running flags, refers to CHAR_DEV_xxx */
    unsigned long flags;

    /* Protect the char device read operation */
	struct mutex rlock;
    /* Protect the char device operation */
    struct semaphore req_lock;
    /* For exclusive operations on this structure */
	spinlock_t lock;

    /* skbbuffer for rx data to userspace */
    struct sk_buff_head rx_q;
    /* Wait in chardev read */
    wait_queue_head_t req_wait_q;
    /* Refer counts */
    atomic_t extra_cnt;
    /* handle of the device */
    void* handle;

    /* wrapper operations */
    int (*open) (struct m_dev * m_dev);
    int (*close) (struct m_dev * m_dev);
    int (*send) (struct m_dev *m_dev, u8* buf, u16 len);
    int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, unsigned long arg);
};

/** Declaration of ar_cdev struct */
struct ar_cdev {
    struct list_head list;
    int minor;                  /** Minor id of the char dev */
    int dev_type;               /** Refers to driver_chrdev_type */
    struct cdev *cdev;          /** Point to the cdev */
    struct m_dev *m_dev;        /** Point to the mdev */
    struct kobject kobj;
};

/** The type of the chrdev type */
typedef enum _driver_chrdev_cmd {
    DRIVER_CHRDEV_TYPE_CONSOLE_CMD = 1,
    DRIVER_CHRDEV_TYPE_VENDOR_CMD,
    DRIVER_CHRDEV_TYPE_MAX
} driver_chrdev_cmd;

#if defined(CDEV_SET_PERMISSION)
/** Changes permissions of the dev */
int ar_chardev_chmod(char *name, mode_t mode);

/** Changes ownership of the dev */
int ar_chardev_chown(char *name, uid_t user, gid_t group);
#endif

/** This function creates the char dev */
int ar_register_char_dev(struct ar_cdev *dev, struct class *char_class,
              char *mod_name, char *dev_name);

/** This function deletes the char dev */
int ar_unregister_char_dev(struct ar_cdev *dev, struct class *char_class,
            char *dev_name);

/** This function cleans module */
void ar_chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class);

/** Get the mdev with dev_type */
struct m_dev* ar_chardev_get_mdev(int dev_type);

/**
 * @brief  Allocation of char dev
 *
 * @param               N/A
 *
 * @return              char_dev
 */
struct ar_cdev *ar_alloc_char_dev(void);

#endif /*__AR_CHARDEV_H__*/
